package com.winit.openapi.handler;

import java.util.Iterator;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.dubbo.remoting.TimeoutException;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.winit.common.error.BizException;
import com.winit.common.spi.SPIException;
import com.winit.common.spi.context.CommandContext;
import com.winit.common.spring.SpringMessageLocator;
import com.winit.openapi.constants.ApiConstant;
import com.winit.openapi.constants.ErrorCode;
import com.winit.openapi.controller.BaseController;
import com.winit.openapi.exception.ApiException;
import com.winit.openapi.exception.ValidationException;
import com.winit.openapi.model.RequestMsg;
import com.winit.openapi.model.ResponseMsg;
import com.winit.openapi.util.ParameterAdapterUtils;
import com.winit.openapi.util.RequestUtil;
import com.winit.tracing.client.Tracer;

/**
 * 统一异常处理
 * 
 * @version <pre>
 * Author	Version		Date		Changes
 * xiangqi.zhou 	1.0  		2015年5月5日 	Created
 *
 * </pre>
 * @since 1.
 */
@ControllerAdvice
public class ApiExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(ApiExceptionHandler.class);

    @ExceptionHandler(SPIException.class)
    public ModelAndView handleSPIException(HttpServletRequest request, HttpServletResponse response, SPIException e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        responseMsg.setCode(e.getErrorCode());
        if (StringUtils.isBlank(e.getErrorMsg())) {
            responseMsg.setMsg(e.getErrorCode());
        } else {
            responseMsg.setMsg(e.getErrorMsg());
        }
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }

    @ExceptionHandler(ApiException.class)
    public ModelAndView handleApiException(HttpServletRequest request, HttpServletResponse response, ApiException e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        responseMsg.setCode(e.getErrorCode());
        responseMsg.setMsg(e.getErrorMsg());
        // 添加输出参数错误信息适配 ,只有API版本是2.0才生效，不会影响现有API add by 2016-07-25
        RequestMsg requestMsg = RequestUtil.getRequestMsg(request);
        if (null != requestMsg) {
            String version = requestMsg.getVersion();
            if (ApiConstant.API_VERSION_2.equals(version)) {
                responseMsg.setMsg(ParameterAdapterUtils.handleResponseMsg(responseMsg.getMsg(), requestMsg.getAction()));
            }
        }
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }
    
    @ExceptionHandler(JSONException.class)
    public ModelAndView handleJSONException(HttpServletRequest request, HttpServletResponse response, JSONException e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        String msg = SpringMessageLocator.getMessage(ErrorCode.ILLEGAL_JSON_STRING, CommandContext.getContext()
            .getLocale());
        responseMsg.setCode(ErrorCode.ILLEGAL_JSON_STRING);
        responseMsg.setMsg(msg);
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }

    @ExceptionHandler(ValidationException.class)
    public ModelAndView handleValidationException(HttpServletRequest request, HttpServletResponse response,
                                                  ValidationException e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        responseMsg.setCode(e.getCode());
        responseMsg.setMsg(e.getMsg());
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }
    
    @ExceptionHandler(BizException.class)
    public ModelAndView handleBizException(HttpServletRequest request, HttpServletResponse response, BizException e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        responseMsg.setCode(e.getErrorCode());
        responseMsg.setMsg(e.getErrorMsg());
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }
    
    @ExceptionHandler(ConstraintViolationException.class)
    public ModelAndView handleConstraintViolationException(HttpServletRequest request, HttpServletResponse response,
                                                           ConstraintViolationException e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        // responseMsg.setCode(e.getCode());
        responseMsg.setMsg(e.getMessage());
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }
    
    
    @ExceptionHandler(TimeoutException.class)
    public ModelAndView handleTimeoutException(HttpServletRequest request, TimeoutException e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        String msg = SpringMessageLocator.getMessage(ErrorCode.SERVICE_TIME_OUT_EXCEPTION, CommandContext.getContext().getLocale());
        responseMsg.setCode(ErrorCode.SERVICE_TIME_OUT_EXCEPTION);
        responseMsg.setMsg(msg);
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }
    
    @ExceptionHandler(RpcException.class)
    public ModelAndView handleRpcException(HttpServletRequest request, HttpServletResponse response, RpcException e) {

        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        if (e.getCause() instanceof ConstraintViolationException) {
            ConstraintViolationException ex = (ConstraintViolationException) e.getCause();
            Set<ConstraintViolation<?>> set = ex.getConstraintViolations();
            if (null != set && !set.isEmpty()) {
                @SuppressWarnings("rawtypes")
                Iterator it = set.iterator();
                while (it.hasNext()) {
                    @SuppressWarnings("rawtypes")
                    ConstraintViolation cValidation = (ConstraintViolation) it.next();
                    responseMsg.setCode(ErrorCode.CONSTRAINT_VIOLATION);
                    responseMsg.setMsg(cValidation.getPropertyPath()+" "+cValidation.getMessage());
                    RequestUtil.setResponseMsg(request, responseMsg);
                    return mv;
                }
            }
        }
        String msg = SpringMessageLocator.getMessage(ErrorCode.SERVICE_ACCESS_EXCEPTION, CommandContext.getContext().getLocale());
        responseMsg.setCode(ErrorCode.SERVICE_ACCESS_EXCEPTION);
        responseMsg.setMsg(msg);
        
        logger.error("handleRpcException error:", e);

        // responseMsg.setCode(e.getCode());
        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }

    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, Exception e) {
        ModelAndView mv = new ModelAndView(BaseController.ERROR);
        ResponseMsg responseMsg = getResponseMsg(request);
        String msg = SpringMessageLocator.getMessage(ErrorCode.SYSTEM_ERROR, CommandContext.getContext().getLocale());
        responseMsg.setCode(ErrorCode.SYSTEM_ERROR);
        responseMsg.setMsg(msg);

        String exceptionName = e.getClass().getName();
        if (StringUtils.isNotBlank(exceptionName) && StringUtils.contains(exceptionName, "ClientAbortException")) {
            RequestMsg requestMsg = RequestUtil.getRequestMsg(request);
            String params = "";
            if (requestMsg != null) {
                params = JSONObject.toJSONString(requestMsg);
            }
            logger.error("ClientAbortException,request uri:[" + request.getRequestURI() + "],request params:[" + params
                         + "]");
        } else {
            logger.error("openapi handleException捕获无法解析具体异常信息的异常， error:", e);
            //发送异常信息到API监控平台
            Tracer.setException(e);
        }

        RequestUtil.setResponseMsg(request, responseMsg);
        return mv;
    }

    private ResponseMsg getResponseMsg(HttpServletRequest request) {
        ResponseMsg responseMsg = RequestUtil.getResponseMsg(request);
        if (responseMsg == null) {
            responseMsg = new ResponseMsg();
        }
        return responseMsg;
    }
}
